package com.carpool;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import android.content.Intent;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;

import com.carpool.R;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapView;
import com.google.android.maps.OverlayItem;


public class MyMapActivity extends MapActivity {
	private static final int NEW_PATH = 1;
	private static final int SAVE_PATH = 2;

	private MapView mapView;
	private TextView txtView;

	private static int size;
	private static int counter = 0;	
	private double total_distance = 0; // Miles
	private int total_time = 0; // Minutes

	// Initialized with size var
	private String coordinates[];
	private int ordering[];
	private double distance[]; 
	private int time[];
	private RoutesMatrix graph;

	private ArrayList<String> addresses = new ArrayList<String>();

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.map);
		mapView = (MapView)findViewById(R.id.mapView);
		mapView.setBuiltInZoomControls(true);
		txtView = (TextView)findViewById(R.id.label);

		setSize();
		initVars();

		zeroCoordinates();
		buildCoordinates();
		zeroGraph();
		buildGraph();

		BranchAndBound route = new BranchAndBound(graph, 0, size-1);
		route.execute();
		for (int i=0; i<size; i++) { ordering[i] = (Integer)route.optimumRoute.get(i); }

		createPoints();

		String point1, point2;
		double src_lat, src_long, dest_lat, dest_long;
		GeoPoint srcGeoPoint, destGeoPoint;
		for (int i=0; i<size-1; i++) {
			point1 = coordinates[ordering[i]];
			point2 = coordinates[ordering[i+1]];
			src_lat = Double.parseDouble(point1.substring(0, point1.indexOf(",")));
			src_long = Double.parseDouble(point1.substring(point1.indexOf(",")+1, point1.length()));
			dest_lat = Double.parseDouble(point2.substring(0, point2.indexOf(",")));
			dest_long = Double.parseDouble(point2.substring(point2.indexOf(",")+1, point2.length()));

			srcGeoPoint = new GeoPoint((int)(src_lat * 1E6), (int)(src_long * 1E6));
			destGeoPoint = new GeoPoint((int)(dest_lat * 1E6), (int)(dest_long * 1E6));

			drawPath(srcGeoPoint, destGeoPoint, Color.GREEN);
		}

		mapView.getController().animateTo(new GeoPoint((int)(Double.parseDouble(coordinates[0].substring(0, coordinates[0].indexOf(",")))*1E6), (int)(Double.parseDouble(coordinates[0].substring(coordinates[0].indexOf(",")+1, coordinates[0].length()))*1E6)));
		mapView.getController().setZoom(13);

		//txtView.setText("Route Information:\nDriving distance: " + calculateTotalDistance() + " miles\nDriving time: " + calculateTotalTime() + " minutes");
		txtView.setText("Route information not avaliable in closed beta.");
		//txtView.setText(graph.toString());

	}

	@Override
	protected boolean isRouteDisplayed() {
		return false;
	}

	private void setSize() {
		Bundle bundle = getIntent().getExtras();
		addresses = bundle.getStringArrayList("addresses");
		size = addresses.size();

	}

	private void initVars() {
		coordinates = new String[size];
		ordering = new int[size];
		distance = new double[size]; 
		time = new int[size];
		graph = new RoutesMatrix(size);

	}

	private void zeroCoordinates() {
		for (int i=0; i<size; i++) {
			coordinates[i] = "0";
		}
	}

	private void buildCoordinates() {
		Bundle bundle = getIntent().getExtras();
		if (bundle != null) {
			addresses = bundle.getStringArrayList("addresses");
			for (int i=0; i<size; i++) {
				Geocoder geoCoder = new Geocoder(getBaseContext(), Locale.getDefault());
				try {
					List<Address> geopoint = geoCoder.getFromLocationName(addresses.get(i),1);
					if (geopoint.size() > 0) 
					{
						coordinates[i] = geopoint.get(0).getLatitude() + "," + geopoint.get(0).getLongitude();
					}
				} catch (IOException e) {          
				} catch (NullPointerException e) {
				}
			}
		}

	}

	private void zeroGraph() {
		for (int i=0; i<size; i++) {
			for (int j=0; j<size; j++) {
				graph.setCost(i, j, 0);
			}
		}
	}

	private void buildGraph() {
		double latitude1, longtitude1, latitude2, longtitude2;
		float results[] = new float[1];
		for (int i=0; i<size; i++) {
			for (int j=0; j<size; j++) {
				latitude1 = Double.parseDouble(coordinates[i].substring(0, coordinates[i].indexOf(",")));
				longtitude1 = Double.parseDouble(coordinates[i].substring(coordinates[i].indexOf(",")+1, coordinates[i].length()));
				latitude2 = Double.parseDouble(coordinates[j].substring(0, coordinates[j].indexOf(",")));
				longtitude2 = Double.parseDouble(coordinates[j].substring(coordinates[j].indexOf(",")+1, coordinates[j].length()));
				Location.distanceBetween(latitude1, longtitude1, latitude2, longtitude2, results);
				graph.setCost(i, j, results[0]);
			}
		}
	}

	private void createPoints() {
		String point;
		double latitude, longtitude;
		GeoPoint geopoint;
		MyItemizedOverlay overlay;

		Drawable icon;

		for (int i=0; i<size; i++) {
			point = coordinates[ordering[i]];
			latitude = Double.parseDouble(point.substring(0, point.indexOf(",")));
			longtitude = Double.parseDouble(point.substring(point.indexOf(",")+1, point.length()));
			geopoint = new GeoPoint((int)(latitude*1E6), (int)(longtitude*1E6));

			if (i==0) {
				icon = getResources().getDrawable(R.drawable.home);
			}
			else if (i==size-1) {
				if (coordinates[0].equals(coordinates[size-1])) {
					icon = getResources().getDrawable(R.drawable.home);
				}
				else {
					icon = getResources().getDrawable(getResources().getIdentifier("marker" + i, "drawable", getPackageName()));
				}
			}
			else {
				icon = getResources().getDrawable(getResources().getIdentifier("marker" + i, "drawable", getPackageName()));
			}
			OverlayItem item = new OverlayItem(geopoint, "", "");
			overlay = new MyItemizedOverlay(icon, this, geopoint, ordering[i]);
			overlay.addOverlay(item);
			mapView.getOverlays().add(overlay);
		}

	}

	private void drawPath(GeoPoint src, GeoPoint dest, int color)
	{
		// Connects to map web service
		StringBuilder urlString = new StringBuilder();
		urlString.append("http://maps.google.com/maps?f=d&hl=en");
		urlString.append("&saddr=");
		urlString.append( Double.toString((double)src.getLatitudeE6()/1.0E6 ));
		urlString.append(",");
		urlString.append( Double.toString((double)src.getLongitudeE6()/1.0E6 ));
		urlString.append("&daddr=");
		urlString.append( Double.toString((double)dest.getLatitudeE6()/1.0E6 ));
		urlString.append(",");
		urlString.append( Double.toString((double)dest.getLongitudeE6()/1.0E6 ));
		urlString.append("&ie=UTF8&0&om=0&output=kml");

		Document doc = null;
		HttpURLConnection urlConnection= null;
		URL url = null;

		try
		{
			url = new URL(urlString.toString());
			urlConnection=(HttpURLConnection)url.openConnection();
			urlConnection.setRequestMethod("GET");
			urlConnection.setDoOutput(true);
			urlConnection.setDoInput(true);
			urlConnection.connect();

			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			DocumentBuilder db = dbf.newDocumentBuilder();
			doc = db.parse(urlConnection.getInputStream());

			if(doc.getElementsByTagName("GeometryCollection").getLength() > 0)
			{
				//parseDistanceAndTime(doc);

				String path = doc.getElementsByTagName("GeometryCollection").item(0).getFirstChild().getFirstChild().getFirstChild().getNodeValue() ;
				String [] pairs = path.split(" ");
				String[] lngLat = pairs[0].split(","); // lngLat[0]=longitude lngLat[1]=latitude lngLat[2]=height

				GeoPoint startGP = new GeoPoint((int)(Double.parseDouble(lngLat[1])*1E6), (int)(Double.parseDouble(lngLat[0])*1E6));
				GeoPoint gp1;
				GeoPoint gp2 = startGP;
				for(int i=1;i<pairs.length;i++) // the last one would be crash
				{
					lngLat = pairs[i].split(",");
					gp1 = gp2;

					gp2 = new GeoPoint((int)(Double.parseDouble(lngLat[1])*1E6), (int)(Double.parseDouble(lngLat[0])*1E6));
					mapView.getOverlays().add(new MyOverlay(gp1,gp2,2,color));
				}
			}
		}
		catch (MalformedURLException e)
		{
			e.printStackTrace();
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
		catch (ParserConfigurationException e)
		{
			e.printStackTrace();
		}
		catch (SAXException e)
		{
			e.printStackTrace();
		}
	}

	public void parseDistanceAndTime(Document doc) {
		int len, minutes;
		double miles;
		String distance, time;

		len = doc.getElementsByTagName("description").getLength();
		distance = doc.getElementsByTagName("description").item(len-1).getFirstChild().getNodeValue();

		miles = Double.parseDouble(distance.substring(10, distance.indexOf("&")));
		this.distance[counter] = miles;

		time = distance.substring(distance.indexOf("(")+7, distance.indexOf(")")); 
		// Requires: time be less than a day
		minutes = 0;
		if (time.indexOf("hour")!=-1) {
			minutes = Integer.parseInt(time.substring(0, time.indexOf(" hour")))*60;
			if (time.indexOf("hours")!=-1)
				time = time.substring(time.indexOf("hours ")+6);
			else
				time = time.substring(time.indexOf("hour ")+5);
		}
		if (time.indexOf("min")!=-1) {
			minutes += Integer.parseInt(time.substring(0, time.indexOf(" ")));
		}
		this.time[counter] = minutes;

		counter++;
	}

	public double calculateTotalDistance() {
		for (int i=0; i<size; i++) {
			total_distance += distance[i];
		}
		return total_distance;
	}

	public int calculateTotalTime() {
		for (int i=0; i<size; i++) {
			total_time += time[i];
		}
		return total_time;
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		boolean result = super.onCreateOptionsMenu(menu);
		menu.add(0, NEW_PATH, Menu.NONE, R.string.newPath);
		menu.add(1, SAVE_PATH, Menu.NONE, R.string.savePath);
		return result;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case NEW_PATH:
			Intent intent =  new Intent(getApplicationContext(), MyStep1Activity.class);
			startActivity(intent);
			finish();
			break;
		case SAVE_PATH:
			finish();
			break;
		}
		return super.onOptionsItemSelected(item);
	}

	@Override
	public void onBackPressed() {
		return;

	}

}